package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.config.WebSocketServer;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.AlertNotifyDto;
import com.zzyl.dto.AlertRuleDto;
import com.zzyl.dto.FloorDto;
import com.zzyl.entity.AlertData;
import com.zzyl.entity.AlertRule;
import com.zzyl.mapper.AlertDataMapper;
import com.zzyl.mapper.AlertMapper;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.service.AlertService;
import com.zzyl.vo.AlertRuleVo;
import com.zzyl.vo.DeviceDataVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;


import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class AlertServictImpl implements AlertService {
    private static final Logger log = LoggerFactory.getLogger(AlertServictImpl.class);

    @Autowired
    private AlertMapper alertMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    //设备维护人员的角色名称
    @Value("${zzyl.alert.deviceMaintainerRole}")
    private String deviceMaintainerRole;
    //超级管理员的角色名称
    @Value("${zzyl.alert.managerRole}")
    private String managerRole;

    @Override
    public void addAlert(AlertRuleDto alertRuleDto) {
        AlertRule bean = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertMapper.inster(bean);
    }

    @Override
    public PageResponse<AlertRuleVo> pageQuery(Integer pageNum, Integer pageSize, String alertRuleName, String functionName, String productKey) {
        PageHelper.startPage(pageNum, pageSize);
        Page<AlertRuleVo> page = alertMapper.pageQuery(alertRuleName, functionName, productKey);
        page.getResult().forEach(item -> {
            String format = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", item.getFunctionName(), item.getOperator(), item.getValue(), item.getDuration());
            item.setRules(format);
        });
        PageResponse<AlertRuleVo> alertRuleVoPageResponse = PageResponse.of(page, AlertRuleVo.class);
        return alertRuleVoPageResponse;
    }

    @Override
    public void deleteAlert(Long id) {
        alertMapper.deleteAlert(id);
    }

    @Override
    public void updateAlert(AlertRuleDto alertRuleDto) {
        AlertRule bean = BeanUtil.toBean(alertRuleDto, AlertRule.class);
        alertMapper.updateAlert(bean);
    }

    @Override
    public void alertRuleData(DeviceDataVo deviceDataVo) {
        //先查看设备数据是不是新鲜的
        LocalDateTime alarmTime = deviceDataVo.getAlarmTime();
        long between = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if (between > 60) {
            return;
        }
        //获取所有设备规则
        List<AlertRuleVo> allAlertRules = alertMapper.selectAllAlertRule("-1", deviceDataVo.getFunctionId(), deviceDataVo.getProductKey());
        //获取单个设备规则
        List<AlertRuleVo> alertRule = alertMapper.selectAllAlertRule(deviceDataVo.getIotId(), deviceDataVo.getFunctionId(), deviceDataVo.getProductKey());
        //把他俩整合到一起
        Collection<AlertRuleVo> alertRuleVos = CollectionUtil.addAll(allAlertRules, alertRule);
        //判断是否为空
        if (CollectionUtil.isEmpty(alertRuleVos)) {
            return;
        }
        //遍历规则 跟遍历设备数据
        alertRuleVos.forEach(alertRuleVo -> {
            alertRuleDataHandler(alertRuleVo, deviceDataVo);
        });

    }

    private void alertRuleDataHandler(AlertRuleVo alertRuleVo, DeviceDataVo deviceDataVo) {
        //1.判断是否在生效时间内
        //后去持续时间
        String[] split = alertRuleVo.getAlertEffectivePeriod().split("~");
        LocalTime startTime = LocalTime.parse(split[0]);
        LocalTime endTime = LocalTime.parse(split[1]);
        //判断数据生成时间是否在生效时间内
        LocalTime localTime = (deviceDataVo.getAlarmTime()).toLocalTime();
        if (localTime.isBefore(startTime) || localTime.isAfter(endTime)) {
            return;
        }
        //2.检验数据是否达到阈值内
        Float ruleValue = alertRuleVo.getValue();//阈值
        alertRuleVo.getOperator();//运算符
        Float dataValue = Float.valueOf(deviceDataVo.getDataValue());//数据的值
        //工具类 x>y---1 x=y----0 x<y-----1
        int compare = NumberUtil.compare(dataValue, ruleValue);
        //设置redis中的标识
        String dataKey = deviceDataVo.getIotId() + ":" + deviceDataVo.getProductKey() + ":" + deviceDataVo.getFunctionId();
        //判断是否满足条件
        //compare>=0 当数据值大于或者等于阈值  运算符必定为>=          compare<0 数据值小于阈值  运算符必定为 <
        if ((compare >= 0 && alertRuleVo.getOperator().equals(">=")) || (compare < 0 && alertRuleVo.getOperator().equals("<"))) {
            log.debug("数据达到阈值");
        } else {
            redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
            return;
        }
        //3.判断沉默周期
        //获取沉默周期
        String silent = (String) redisTemplate.opsForValue().get(CacheConstants.ALERT_SILENT_PREFIX + dataKey);
        //判断redis中的沉默周期是否为空 为空 继续执行 不为空 执行返回执行下一条数据
        if (ObjectUtil.isNotEmpty(silent)) {
            return;
        }

        //4.在redis中设置一个触发次数的标识
        String countkey = redisTemplate.opsForValue().get(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX+ dataKey);
        int count = StringUtils.isEmpty(countkey) ? 1 : Integer.parseInt(countkey) + 1;
        //判断触发次数是否达到持续周期值
        if (count != alertRuleVo.getDuration()) {
            //没达到 就把值存入redis中 把count的值给count可以 再继续下一条数据
            redisTemplate.opsForValue().set(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey, count + "");
            return;
        }
        //连续达到持续周期值
        //清空redis中的countkey
        redisTemplate.delete(CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + dataKey);
        //在redis中添加一个五分钟的沉默周期 标识
        redisTemplate.opsForValue().set(CacheConstants.ALERT_SILENT_PREFIX + dataKey, "aaa", alertRuleVo.getAlertSilentPeriod(), TimeUnit.MINUTES);

        List<Long> userId = null;
        //判断是否是老人 0老人 1设备
        if (alertRuleVo.getAlertDataType().equals(1)) {
            //不是 通知维修员
            //获取维修员id 通过角色名查询id
             userId = alertMapper.getuserid(deviceMaintainerRole);
        } else {// 是老人
            //判断是什么设备
            if(deviceDataVo.getLocationType() == 0){
                //是老人 通过老人查护理员
                userId =  deviceMapper.selectNursingIdsByIotIdWithElder(managerRole);
            }else if(deviceDataVo.getLocationType() == 1 && deviceDataVo.getPhysicalLocationType() == 2){
                //是床 通过床位查询护理员
                userId =deviceMapper.selectNursingIdsByIotIdWithBed(managerRole);
            }
        }
        //通知超级管理员
        List<Long> managerIds = alertMapper.getuserid(managerRole);
        List<Long> allConsumerIds = CollUtil.addAllIfNotContains(userId, managerIds);
        allConsumerIds = CollUtil.distinct(allConsumerIds);
        insertAlertData(deviceDataVo, alertRuleVo, allConsumerIds);
    }
    @Autowired
    private AlertDataMapper alertDataMapper;
    @Autowired
    private WebSocketServer socketServer;
    private void insertAlertData(DeviceDataVo deviceDataVo, AlertRuleVo alertRuleVo, List<Long> allConsumerIds) {
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", alertRuleVo.getFunctionName(), alertRuleVo.getOperator(), alertRuleVo.getValue(), alertRuleVo.getDuration());
        AlertData alertData = BeanUtil.toBean(deviceDataVo, AlertData.class);
        alertData.setAlertRuleId(alertRuleVo.getId());
        alertData.setAlertReason(alertReason);
        alertData.setType(alertRuleVo.getAlertDataType());
        alertData.setStatus(0);

        List<AlertData> list = allConsumerIds.stream().map(id -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(id);
            return dbAlertData;
        }).collect(Collectors.toList());
        alertDataMapper.batchInsert(list);

        //报警通知数据发送给
        //我接受的异常数据后 要根据类型 把userId（维修工、护理员）  managerIds（超级管理员）
        AlertNotifyDto alertNotifyDto = new AlertNotifyDto();
        AlertData alertData1 = list.get(0);
        alertNotifyDto = BeanUtil.toBean(alertData1, AlertNotifyDto.class);
        alertNotifyDto.setFunctionName(alertRuleVo.getFunctionName());
        alertNotifyDto.setAlertDataType(alertData.getType());
        alertNotifyDto.setNotifyType(1);
        socketServer.sendMessageToConsumer(alertNotifyDto,allConsumerIds);
    }
}
